//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <mantle.h>
#include <clsinfo.h>
#include <stub.h>
#include <kmarshal.h>
#include <syscall.h>
#include <kprxstub.h>
#include <ckernel.h>
#include <svrobj.h>
#include "rot.h"

EXTERN_C const InterfaceID EIID_IProxy;
EXTERN_C const InterfaceID EIID_IInterface;
EXTERN_C const InterfaceID EIID_IClassObject;
EXTERN_C const InterfaceID EIID_CLASS_INFO;

static CKernel s_CKernel;
CKernel * g_pCKernel = &s_CKernel;
extern CIClassInfo *g_elasys_classInfo;

ECode KernUnmarshalInterface(
    /* [in] */ InterfacePack *pInterfacePack,
    /* [in] */ PContext pContext,
    /* [out] */ PInterface *ppObj)
{
    ExportObject ex;
    ImportObject im;
    ECode ec;
    IProxy *pIProxy;

    g_marshalLock.Lock();

    ec = FindKernExportObject(pInterfacePack->m_oid, &ex);
    if (SUCCEEDED(ec)) {
        g_marshalLock.Unlock();

        assert(pInterfacePack->m_pid == KERNEL_PID);

        CObjectStub* pSt = (CObjectStub*)ex.m_pIStub;
        *ppObj = pSt->m_pInterfaces[pInterfacePack->m_uIndex].m_pObject;
        (*ppObj)->AddRef();
        ex.m_pIStub->Release();
        ex.m_pIStub->Release();
        return NOERROR;
    }

    ec = FindKernImportObject(pInterfacePack->m_oid, &im);
    if (SUCCEEDED(ec)) {
        g_marshalLock.Unlock();
        ec = im.m_pIProxy->IndexQI(pInterfacePack->m_uIndex, ppObj);
        im.m_pIProxy->Release();
        DetachServer(pInterfacePack->m_oid, CTX_KERN_2_USER);
    }
    else {
        ec = CObjectProxy::S_CreateObject(
            pInterfacePack->m_oid,
            pInterfacePack->m_clsid,
            (UInt32 *)pInterfacePack->m_pClsEntry,
            CTX_KERN_2_USER,
            &pIProxy);
        g_marshalLock.Unlock();
        if (SUCCEEDED(ec)) {
            ec = pIProxy->IndexQI(pInterfacePack->m_uIndex, ppObj);
            pIProxy->Release(); // Release IProxy's ref count
        }
    }

    return ec;
}

ECode KernMarshalInterface(
    /* [in] */ PInterface pObj,
    /* [out] */ InterfacePack *pInterfacePack)
{
    ExportObject ex;
    ECode ec;
    IStub *pIStub;
    IProxy *pv;

    g_marshalLock.Lock();
    ec = FindKernExportObject(pObj, &ex);
    if (SUCCEEDED(ec)) {
        ex.m_pIStub->GetOid(&pInterfacePack->m_oid);
        pInterfacePack->m_pid = KERNEL_PID;
        ex.m_pIStub->GetClsid(&pInterfacePack->m_clsid);
        ex.m_pIStub->GetClassEntry(&pInterfacePack->m_pClsEntry);
        ec = ex.m_pIStub->InterfaceIndex(pObj, &pInterfacePack->m_uIndex);
        g_marshalLock.Unlock();
    }
    else {
        pv = (IProxy*)pObj->Probe(EIID_IProxy);
        if (pv) {
            pv->AddRef();
            g_marshalLock.Unlock();
            pv->GetOid(&pInterfacePack->m_oid);
            pInterfacePack->m_pid = INVALID_PID;
            pv->GetClsid(&pInterfacePack->m_clsid);
            pv->GetClassEntry(&pInterfacePack->m_pClsEntry);
            pv->QI_Index((IInterface**)&pObj, &pInterfacePack->m_uIndex);
            pv->Release(); // we don't release the reference remotely
            AttachServer(pInterfacePack->m_oid, CTX_DIFF_PROCESS);
            ec = NOERROR;
        }
        else {
            ec = CObjectStub::S_CreateObject(pObj, &pIStub);
            if (SUCCEEDED(ec)) {
                pIStub->GetOid(&pInterfacePack->m_oid);
                pInterfacePack->m_pid = KERNEL_PID;
                pIStub->GetClsid(&pInterfacePack->m_clsid);
                pIStub->GetClassEntry(&pInterfacePack->m_pClsEntry);
                ec = pIStub->InterfaceIndex(pObj, &pInterfacePack->m_uIndex);
            }
            g_marshalLock.Unlock();
        }
    }
    return ec;
}

EXTERN ECode DzFindService(const wchar_t *pwszName, PInterface *ppObj)
{
    CObjectStub *   pCStub;
    NameHook *      pNameHook;
    Object *        pObject;
    IProxy *        pIProxy;
    ImportObject    im;
    ECode           ec;

    assert(pwszName && ppObj);

    g_marshalLock.Lock();

    pNameHook = NameHook::Find(pwszName);
    if (NULL == pNameHook) {
        g_marshalLock.Unlock();
        return E_DOES_NOT_EXIST;
    }
    pObject = pNameHook->m_pObject;
    assert(pObject);
    if (pObject->m_pCProcess) {
        // It's a user register object

        // TODO: here need further analyse
        assert(pObject->m_pClsInfo);

        ec = FindKernImportObject(pObject->m_oid, &im);
        if (SUCCEEDED(ec)) {
            // Proxy imported

            g_marshalLock.Unlock();

            ec = im.m_pIProxy->IndexQI(pObject->m_uIndex, ppObj);
            im.m_pIProxy->Release();
        }
        else {
            // Create a proxy

            ec = AttachServer(pObject->m_oid, CTX_KERN_2_USER);
            if (FAILED(ec)) {
                g_marshalLock.Unlock();
                return ec;
            }

            ec = CObjectProxy::S_CreateObject(
                        pObject->m_oid,
                        pObject->m_clsid,
                        (UInt32 *)pObject->m_pClsInfo,
                        CTX_KERN_2_USER,
                        &pIProxy);

            g_marshalLock.Unlock();

            if (SUCCEEDED(ec)) {
                ec = pIProxy->IndexQI(pObject->m_uIndex, ppObj);
                pIProxy->Release(); // Release IProxy's ref count
            }
        }
    }
    else {
        // It is a system object

        pCStub = (CObjectStub *)pNameHook->m_pObject->m_pIStub;
        *ppObj = \
            (pCStub->m_pInterfaces[pNameHook->m_pObject->m_uIndex]).m_pObject;
        (*ppObj)->AddRef();
        g_marshalLock.Unlock();
        ec = NOERROR;
    }

    return ec;
}

bool_t DECL_CODEINIT RegisterKernelInterface()
{
    ECode ec;
    CIClassInfo * pKerClassInfo;
    ISharedMemory * pISharedMem;

    assert(4 * PAGE_SIZE >= (UInt32)g_elasys_classInfo->totalSize);
    pKerClassInfo = (CIClassInfo *)DzAllocKernelPages(2); // alloc 4 pages
//        (CIClassInfo *)malloc(((CIClassInfo*)g_elasys_classInfo)->totalSize);
    assert(pKerClassInfo);

    ec = InitServerClassInfo();
    if (FAILED(ec)) {
        kprintf("In RegisterKernelInterface, ");
        kprintf("InitServerClassInfo failed, the ec = 0x%08x\n", ec);
        return FALSE;
    }

    InitializeROT();

    RelocateClassInfo(g_elasys_classInfo, pKerClassInfo);

    ec = RegisterServerClassInfo(pKerClassInfo);
    assert(SUCCEEDED(ec));
    if (FAILED(ec)) {
        kprintf("In RegisterKernelInterface, ");
        kprintf("RegisterServerClassInfo failed, the ec = 0x%08x\n", ec);
        return FALSE;
    }

    ec = DzRegisterService(L"ElastosKernel", (PInterface)g_pCKernel, NULL);
    assert(SUCCEEDED(ec));
    if (FAILED(ec)) {
        kprintf("In RegisterKernelInterface, ");
        kprintf("DzRegisterService failed, the ec = 0x%08x\n", ec);
        return FALSE;
    }

    ec = g_pCKernel->CreateSharedMemory(\
                        MAX_SHARED_MEM_OF_CLSINFO, false, &pISharedMem);
    assert(SUCCEEDED(ec));
    if (FAILED(ec)) {
        kprintf("In RegisterKernelInterface, ");
        kprintf("CreateSharedMemory failed, the ec = 0x%08x\n", ec);
        return FALSE;
    }
    ec = DzRegisterService(L"SysSharedClassInfo", (PInterface)pISharedMem, NULL);
    assert(SUCCEEDED(ec));
    if (FAILED(ec)) {
        kprintf("In RegisterKernelInterface, ");
        kprintf("RegisterSharedClassInfo failed, the ec = 0x%08x\n", ec);
        return FALSE;
    }
    return TRUE;
}

#if defined(_DEBUG) || defined(_MARSHAL_DEBUG)
int _DumpGUID(REIID riid)
{
    return kprintf("%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X\n",
        riid.Data1, riid.Data2, riid.Data3,
        riid.Data4[0], riid.Data4[1], riid.Data4[2],
        riid.Data4[3], riid.Data4[4], riid.Data4[5],
        riid.Data4[6], riid.Data4[7]);
}

int _DumpCLSID(RClassID rclsid)
{
    _DumpGUID((REIID)rclsid);
    return kprintf("%S\n", rclsid.pUunm);
}

#endif // _DEBUG || _MARSHAL_DEBUG
